function getClientId () {
        var res="";
        try{
            var localId = window.localStorage.getItem("HHKmsClient.V10");
            if( localId==null){
                localId = "HHKmsClient.V10."+( new Date()).getTime().toString();
                window.localStorage.setItem("HHKmsClient.V10", localId);
            }
            res = localId;
        }
        catch (e) {
            console.log(e);
        }
        return res;
    }
function THHKmsClient() {
    this.config={
        clientId : getClientId(),
        clientInfo:{},
        localVideoId:"",
        remoteVideoId:"",
        kurentUrl: "wss://wss.hhdata.cn:8433/kurento",
        kmsPeerOptions: {
            configuration: {
                "iceServers": [
                    {
                        urls: "stun:223.68.161.139:3478"
                    },
                    {
                        urls: ["turn:223.68.161.139:3478"],
                        username: "kurento",
                        credential: "kurento"
                    }
                ]
            },
            mediaConstraints: {
                audio: true,
                video: {width: {exact: 1280}, height: {exact: 720}}
            },
            localVideo: null,
            remoteVideo: null
        },
        kmsPeerTypes: {
            send: kurentoUtils.WebRtcPeer.WebRtcPeerSendonly,
            rec: kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly,
            sendRec: kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv
        },
        withHubFlag:1 ,
        roomId:"",
        roomPlayers:[
            /*
            {
                id:"",
                url:"",
                tag:{}
            }
            */
        ],
        mqClient: {
            host:"gmggf8g.mqtt.iot.bj.baidubce.com",
            port:443,
            uid:"gmggf8g/peer",
            pwd:"a52bwKg22dCAuQzB",
            rootTopic:"hhukms/msg",
            willTopic:"hhukms/disconnect"
        }
    };
    this.data={
        kurentClient: null,
        pipe:null ,
        hub:null ,
        end:null,
        hubPort:null,
        players:{},
        sessions:{},
        mqClient:null,
        peer:null,
        devices:{},
        localVideoStatus:{
            status: 0,
            cameraIds:[]
        }
    };
    this.events={
        onSessionOnline: function () {},
        onSessionOffline: function () {},
        onRecMqMessage: function (topic, msgInfo) {},
    };
    this.cb=function (callbak, p0,p1,p2) {
        try{
            if( callbak){
                callbak(p0,p1,p2);
            }
        }catch (e) {
            console.log(e);
        }
    };
    this.kmsGetClient = function (callback) {
        var me = this;
        try {
            if (me.data.kurentClient) {
                me.cb(callback, me.kurentClient);
            } else {
                kurentoClient(me.config.kurentUrl, function (error, client) {
                    if (client) {
                        me.data.kurentClient = client;
                    }
                    me.cb(callback, client);
                });
            }
        } catch (er) {
            console.log(er);
        }
    };
    this.kmsLoadObjs = function (callback) {
        var me = this;
        try {
            var loadPipe= function(onLoadPipe){
                try{
                    var getPipeInfo = function (p , onGetPipeInfo) {
                        p.getName(function (erOnGetName, name) {
                            p.roomId = name;
                            onGetPipeInfo();
                        })
                    };
                    me.data.kurentClient.getServerManager(function (erSvr , svr) {
                        svr.getPipelines(function (erPs , pipes) {
                            var  index =0;
                            var findPipe= function () {
                                if( index<pipes.length){
                                    var p = pipes[index];
                                    getPipeInfo(p , function () {
                                        if(p.roomId && p.roomId==me.config.roomId){
                                            me.data.pipe = p;
                                            me.cb(onLoadPipe);
                                        }
                                        else{
                                            index++;
                                            findPipe();
                                        }
                                    })
                                }
                                else{
                                    me.data.kurentClient.create('MediaPipeline', function (erNewPipe , p) {
                                        p.setName(me.config.roomId, function (erSetName) {
                                            p.roomId = me.config.roomId;
                                            me.data.pipe = p;
                                            me.cb(onLoadPipe);
                                        })
                                    })
                                }
                            };
                            findPipe();
                        })
                    })
                }
                catch (e) {
                    console.log(e);
                }
            }
            var loadHub= function(onLoadHub){
                try{
                    me.data.pipe.getChildren(function (erChild , items) {
                        var hubs = items.filter(function (item) {
                            return item.id.indexOf(".Composite")>0;
                        });
                        if( hubs.length>0){
                            me.data.hub = hubs[0];
                            me.cb(onLoadHub);
                        }
                        else{
                            me.data.pipe.create("Composite", function (erHub, hub) {
                                me.data.hub = hub;
                                me.cb(onLoadHub);
                            })
                        }
                    });
                }
                catch (e) {
                    console.log(e);
                }
            }
            var loadPlayers= function(onLoadPlayers){
                try{
                    me.data.pipe.getChildren(function (erItems, items) {
                        var index =0;
                        var scan = function () {
                            if( index<items.length){
                                var item = items[index];
                                if( item.id.indexOf(".PlayerEndpoint")>0){
                                    item.getName(function (erName , name) {
                                        item.playerId = name;
                                        me.data.players[name] = item;
                                        index++;
                                        scan();
                                    });
                                }
                                else{
                                    index++;
                                    scan();
                                }
                            }
                            else {
                                var newItems = me.config.roomPlayers.filter(function (item) {
                                    return !me.data.players[item.id];
                                });
                                index =0;
                                var newPlayer = function () {
                                    if( index<newItems.length) {
                                        var pItem = newItems[index];
                                        me.data.pipe.create("PlayerEndpoint", {uri: pItem.url}, function (erNewPlayer, p) {
                                            p.setName(pItem.id, function (erSetName) {
                                                p.playerId = pItem.id;
                                                p.kmsInfo = pItem;
                                                me.data.players[pItem.id] = p;
                                                index++;
                                                newPlayer();
                                            })
                                        })
                                    }
                                    else{
                                        for( k in me.data.players){
                                            var player = me.data.players[k];
                                            var arrs  = me.config.roomPlayers.filter(function (item) {
                                                return item.id == player.playerId;
                                            });
                                            player.tag = arrs.length>0?arrs[0]:{};
                                        }
                                        me.cb(onLoadPlayers);
                                    }
                                }
                                newPlayer();
                            }
                        };
                        scan();
                    });
                }
                catch (e) {
                    console.log(e);
                }
            }
            var createEnd= function(onCreateEnd){
                try{
                    me.data.pipe.create("WebRtcEndpoint", function (erEnd, end) {
                        end.addTag('kmsInfo', JSON.stringify(me.config.clientInfo)).then(()=>{
                            return end.setMaxVideoRecvBandwidth(3000);
                        }).then(()=>{
                            return end.setMaxVideoSendBandwidth(3000);
                        }).then(()=>{
                            return end.setMinVideoRecvBandwidth(1500);
                        }).then(()=>{
                            return end.setMinVideoSendBandwidth(1500);
                        }).then(()=>{
                            me.data.end = end;
                            me.cb(onCreateEnd);
                        });
                    })
                }
                catch (e) {
                    console.log(e);
                }
            }
            var createHubport= function(onCreateHubport){
                try{
                    if( me.data.hub!=null  && me.data.end!=null){
                        me.data.hub.createHubPort(function (erPort , hubPort) {
                            me.data.end.connect(hubPort);
                            me.data.hubPort = hubPort;
                            me.cb(onCreateHubport);
                        })
                    }
                }
                catch (e) {
                    console.log(e);
                }
            }
            var loadDevices = function(onLoadDevices){
                try{
                    var res = {};
                    navigator.mediaDevices.enumerateDevices().then(function (devs) {
                        for (var i = 0; i < devs.length; i++) {
                            var item = devs[i];
                            if (!res[item.kind]) {
                                res[item.kind] = []
                            }
                            res[item.kind].push(item);
                        }
                        me.data.devices = res;
                        me.data.localVideoStatus.cameraIds.push(res.videoinput[0].deviceId);
                        me.data.localVideoStatus.cameraIds.push(res.videoinput[res.videoinput.length -1].deviceId);
                        me.cb(onLoadDevices );
                    });
                }
                catch (e) {
                    console.log(e);
                }
            }

            me.kmsGetClient(function (client) {
                loadPipe(function () {
                    loadPlayers(function () {
                        loadDevices(function () {
                            createEnd(function () {
                                if(me.config.withHubFlag){
                                    loadHub(function () {
                                        createHubport(function () {
                                            me.cb(callback);
                                        })
                                    })
                                }
                                else{
                                    me.cb(callback);
                                }
                            })
                        })
                    })
                })
            })
        } catch (er) {
            console.log(er);
        }
    };
    this.kmsBindPeerAndEndpoint = function (peer, endpoint, callback) {
        var me = this;
        try {
            var setCandidateEvent = function (peer, endpoint) {
                try {
                    if (peer && endpoint) {
                        endpoint.on('OnIceCandidate', function (event) {
                            var candidate = event.candidate;
                            peer.addIceCandidate(candidate);
                            console.log('candidate w->p')
                        });
                        peer.on('icecandidate', function (candidate) {
                            var iceCandidate = kurentoClient.register.complexTypes.IceCandidate(candidate);
                            endpoint.addIceCandidate(iceCandidate);
                            console.log('candidate p->w')

                        });
                    }
                } catch (e) {
                    ;
                }
            }
            if (endpoint && peer) {
                peer.generateOffer(function (errorPeerGenerateOffer, sdpOffer) {
                    if (errorPeerGenerateOffer && callback) {
                        callback(errorPeerGenerateOffer, null);
                    } else {
                        endpoint.processOffer(sdpOffer, function (errorSdpOffer, sdpAnswer) {
                            if (errorSdpOffer && callback) {
                                console.log("process sdpOffer error.", errorSdpOffer)
                                callback(errorSdpOffer, null);
                            } else {
                                setCandidateEvent(peer, endpoint);
                                peer.processAnswer(sdpAnswer);
                                endpoint.gatherCandidates(function (errorGatherCandidates) {
                                    if (errorGatherCandidates && callback) {
                                        console.log('webRtcPoint gatherCandidates error.', errorGatherCandidates)
                                        callback(errorGatherCandidates, null);
                                    } else {
                                        peer.endpoint = endpoint;
                                        endpoint.peer = peer;
                                        setCandidateEvent(peer, endpoint);
                                        if (callback) {
                                            callback(null, {endpoint: endpoint, peer: peer});
                                        }
                                    }
                                });
                            }
                        });
                    }
                });
            } else {
                if (callback) {
                    callback({error: "endPoint or peer is null."}, null);
                }
            }
        } catch (er) {
            console.log(er, "bindPeerAndEndpoint error.");
        }
    };
    this.kmsOpenPeer = function (callback) {
        var me = this;
        try {
            var opts = JSON.parse(JSON.stringify(me.config.kmsPeerOptions));
            opts.localVideo = document.getElementById(me.config.localVideoId);
            opts.remoteVideo = document.getElementById(me.config.remoteVideoId);
            //opts.mediaConstraints.video = false;
            var peer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(opts, function () {
                me.kmsBindPeerAndEndpoint(peer , me.data.end, function () {
                    me.data.peer = peer;
                    me.data.cameraIndex = -1;
                    me.cb(callback);
                });
            })
        } catch (er) {
            console.log(er);
        }
    };
    this.shareScreen = function ( ) {
        var me = this;
        try {
            var screenConstraints = {video: {width: {max: 1280}, height: {max: 720}}, audio: true};
            var peer = me.data.peer;
            var sender= null;
            if (navigator.mediaDevices.getDisplayMedia) {
                navigator.mediaDevices.getDisplayMedia(screenConstraints).then(
                    function (stream) {
                        let videoTrack = stream.getVideoTracks()[0];
                        peer.localVideo.srcObject = stream;
                        sender = peer.peerConnection.getSenders().find(function (s) {
                            return s.track.kind == videoTrack.kind;
                        });
                        sender.track.stop();
                        sender.replaceTrack(videoTrack);
                    }
                )
            }
        } catch (er) {
            console.log(er);
        }
    };
    this.setCamera = function ( newCameraDeviceId ) {
        var me = this;
        try {
            if (window.rtcVideoStream) {
                window.rtcVideoStream.getTracks().forEach(track => {
                    if( track.kind=="video") {
                        track.stop();
                    }
                });
            }
            var constraints = {video: {deviceId: newCameraDeviceId , height:{exact:720} , width:{exact:1280}}};
            var peer = me.data.peer;
            var sender= null;
            navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
                window.rtcVideoStream = stream;
                peer.localVideo.srcObject = stream;
                let videoTrack = stream.getVideoTracks()[0];
                sender = peer.peerConnection.getSenders().find(function (s) {
                    return s.track.kind == videoTrack.kind;
                });
                sender.replaceTrack(videoTrack);
            });
        } catch (er) {
            console.log(er);
        }
    };
    this.mqConnect = function (callback) {
        var me = this;
        try {
            if( me.data.mqClient!=null){
                me.data.mqClient.Client.disconnect();
                me.data.mqClient.Client = null;
                me.data.mqClient = null;
            }
            var onConnect = function(ws){
                //var topic = me.datas.msConfig.rootTopic+"/task."+me.datas.info.F_TaskID.toString()+"/#";
                var roomTopic = [me.config.mqClient.rootTopic,'room', me.config.roomId.toString(),'#'].join('/').replace("//",'/');
                var clientTopic = [me.config.mqClient.rootTopic,'client',me.data.mqClient.clientId,'#'].join('/').replace("//",'/');
                me.data.mqClient.Subcribe(roomTopic,0);
                me.data.mqClient.Subcribe(clientTopic,0);
                me.cb(callback , 'connected');
                console.log('connect',ws);
                var msg = me.getNotifyMsg();
                var hello = function(){
                    msg.ac = "hello";
                    msgTopic =  [me.config.mqClient.rootTopic,'room', me.config.roomId.toString(),"hello"].join('/').replace("//",'/');
                    me.data.mqClient.Publish(msgTopic , JSON.stringify(msg), 0, false);
                }
                window.setTimeout( hello , 2000);
            };
            var onDisConnect = function(ws){
                console.log('disconnect',ws);
                me.cb(callback , 'disconnected');
            };
            var onRecMsg = function(  msg){
                try {
                    var msgInfo = JSON.parse(msg.MsgContent);
                    if( msgInfo.ac =="disconnectNotify"){
                        if( me.data.sessions[msgInfo.cId]){
                            delete me.data.sessions[msgInfo.cId];
                        }
                        me.cb(me.events.onSessionOffline , msgInfo);
                    }
                    else if( msgInfo.ac =="hello"){
                        me.data.sessions[msgInfo.cId] = msgInfo;
                        var msg = me.getNotifyMsg();
                        msg.ac ="welcome";
                        var clientTopic = [me.config.mqClient.rootTopic,'client', msgInfo.cId].join('/').replace("//",'/');
                        me.data.mqClient.Publish(clientTopic , JSON.stringify(msg), 0, false);
                        if( msgInfo.cId != me.data.mqClient.clientId) {
                            me.cb(me.events.onSessionOnline, msgInfo);
                        }
                    }
                    else if( msgInfo.ac =="welcome"){
                        me.data.sessions[msgInfo.cId] = msgInfo;
                        me.cb(me.events.onSessionOnline , msgInfo);
                    }
                    else{
                        me.cb(me.events.onRecMqMessage, msg.topic , msgInfo);
                    }
                    //console.log(msgInfo);
                }
                catch (er) {
                    console.log(er);
                }
            };
            var willTopic = [
                me.config.mqClient.rootTopic,'room', me.config.roomId.toString(), 'disconnect'
            ].join('/').replace("//","/");
            var willInfo = me.getNotifyMsg();
            me.data.mqClient = newMqttClient(
                me.config.mqClient.host ,
                me.config.mqClient.port ,
                me.config.mqClient.uid ,
                me.config.mqClient.pwd ,
                willTopic,
                willInfo,
                onConnect , onDisConnect , onRecMsg
            );
            me.data.mqClient.Connect();
        } catch (er) {
            console.log(er);
        }
    };
    this.getNotifyMsg = function () {
        var me = this;
        var res={};
        try {
            res.localId = me.config.clientId;
            res.cId = ( me.data.mqClient && me.data.mqClient!=null)?me.data.mqClient.clientId:"";
            res.roomId = me.config.roomId;
            res.kmsPipeId = me.data.pipe.id;
            res.kmsEndId = me.data.end.id;
            res.kmsHubportId = (me.data.hubPort && me.data.hubPort!=null)?me.data.hubPort.id : "";
            res.tag = me.config.clientInfo;
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.openVideo = function () {
        var me = this;
        var res={};
        try {
            var status= me.data.localVideoStatus.status ;
            me.closeVideo();
            me.data.localVideoStatus.status = status;
            if( me.data.localVideoStatus.status==0){
                ;
            }
            else if( me.data.localVideoStatus.status==1 || me.data.localVideoStatus.status==2 ){
                //me.data.peer.peerConnection.localVideo.videoEnabled = true;
                var devId = me.data.localVideoStatus.cameraIds[me.data.localVideoStatus.status-1];

                var constraints = {video: {deviceId: devId}};
                var sender= null;
                navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
                    window.rtcVideoStream = stream;
                    me.data.peer.localVideo.srcObject = stream;
                    let videoTrack = stream.getVideoTracks()[0];
                    sender = me.data.peer.peerConnection.getSenders().find(function (s) {
                        return s.track.kind == videoTrack.kind;
                    });
                    sender.replaceTrack(videoTrack);
                });
            }
            else if( me.data.localVideoStatus.status==3){
                //me.data.peer.localVideo.videoEnabled = true;
                var screenConstraints = {video: {width: {max: 1280}, height: {max: 720}}, audio: true};
                var sender= null;
                if (navigator.mediaDevices.getDisplayMedia) {
                    //navigator.mediaDevices.getDisplayMedia({video:true}).then(
                    navigator.mediaDevices.getDisplayMedia(screenConstraints).then(
                        function (stream) {
                            let videoTrack = stream.getVideoTracks()[0];
                            me.data.peer.localVideo.srcObject = stream;
                            sender = me.data.peer.peerConnection.getSenders().find(function (s) {
                                return s.track.kind == videoTrack.kind;
                            });
                            sender.track.stop();
                            sender.replaceTrack(videoTrack);
                        }
                    )
                }
            }
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.openCameraFront = function () {
        var me = this;
        var res={};
        try {
            me.data.localVideoStatus.status=1;
            me.openVideo();
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.openCameraBack = function () {
        var me = this;
        var res={};
        try {
            me.data.localVideoStatus.status=2;
            me.openVideo();
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.shareScreen = function () {
        var me = this;
        var res={};
        try {
            me.data.localVideoStatus.status=3;
            me.openVideo();
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.closeVideo = function () {
        var me = this;
        var res={};
        try {
            me.data.localVideoStatus.status =0;
            if (window.rtcVideoStream) {
                window.rtcVideoStream.getTracks().forEach(track => {
                    if( track.kind=="video") {
                        track.stop();
                    }
                });
            }
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.toggleAudio = function () {
        var me = this;
        var res={};
        try {
            var enable = me.data.peer.localVideo.volume = 0?1:0;
        } catch (er) {
            console.log(er);
        }
        return res;
    };
    this.acHello = function () {
        var me = this;
        var res={};
        try {
            var msg = me.getNotifyMsg();
            msg.ac = "hello";
            msgTopic =  [me.config.mqClient.rootTopic,'room', me.config.roomId.toString(),"hello"].join('/').replace("//",'/');
            me.data.mqClient.Publish(msgTopic , JSON.stringify(msg), 0, false);
        } catch (er) {
            console.log(er);
        }
        return res;
    };

}